"""
通讯协议设置(客户端):
    进入聊天群: L代号
    聊天: C代号
    退出: Q代号
    服务器反馈: ok 表示成功, 其他表示失败.

"""

import os
import sys
from socket import *
from signal import *
import time


class ChatChilent:
    def __init__(self):
        self.__sockfd = socket(AF_INET, SOCK_DGRAM)
        self.__server_ip = ("127.0.0.1", 18693)

        # 定义通讯协议
        self.__msg = 'L '  # 进入群聊
        self.__group_chat = 'C '  # 聊天
        self.__quit = 'Q '  # 退出

    # 子进程处理函数,发送消息
    def __send_msg(self, name):
        """聊天群"""
        while True:
            try:
                tex = input(f"你,{name}>:")
            except KeyboardInterrupt: # 如果客户端Ctrl + c 结束程序的话, 直接赋值exit给tex变量后续给服务器发送退出请求
                tex = 'exit'
            
            
            if not tex:
                continue
            elif tex.strip() == "exit": # ''.strip() 去掉两侧空格
                self.__sockfd.sendto(f"Q {name}".encode(), self.__server_ip)  # 给服务器发送退出消息
                
                sys.exit("退出群聊.")
            
            self.__sockfd.sendto(f"C {name} {tex}".encode(), self.__server_ip)  # 发送消息
            
    
    # 父进程处理函数, 接收消息
    def __recv_msg(self, name):
        """处理接收消息"""
        # 接收处理退出函数,这里主要是主进程函数退出
        while True:
            try:
                data, addr = self.__sockfd.recvfrom(9216)
            except KeyboardInterrupt: # 如果客户端Ctrl + c 结束程序的话, 直接赋值exit给tex变量后续给服务器发送退出请求
                data = 'exit'
            
            if data == 'exit' or data.decode() == 'exit':  # 从服务器收到exit,则退出父进程, 否则一直卡进程
                sys.exit()
                
            print(f"\n群聊消息->{data.decode()}" + f"\n你,{name}>:", end='')  # 因为父子进程干扰同一行,第一次发言没有影响,但是第二发言会造成断行, 所以换行伪装一下提示是自己发言
            
        self.__sockfd.close()
        sys.exit("退出了群聊.")
    
    # 加入聊天群
    def __login(self):
        "处理第一次登入服务器,验证姓名,不能相同."
        while True:
            name = input("输入昵称:")
            
            if not name: # 如果为空则返回这一次处理,继续输入
                continue
            elif name == "exit": # 退出
                # 给服务器发送信息
                self.__sockfd.sendto(f"Q exit".encode(), self.__server_ip)
                self.__sockfd.close()
                sys.exit("正常退出!")
                
            # 给服务器发送信息
            self.__sockfd.sendto(f"L {name}".encode(), self.__server_ip)
            
            # 接收反馈结果
            data, addr = self.__sockfd.recvfrom(1024)
            # 按照协议解包
            if data.decode().split(' ')[0] == "ok":
                print(data.decode().split(' ')[0])
                return name  # 成功则返回昵称,进行下一步处理
            else:
                print(data.decode()) # 失败返回服务器消息
                continue
        
    # main
    def start(self):
        """开始, 创建进程分别执行收发消息"""
        
        name = self.__login() # 第一次加入,需要服务器验证通过
        
        # 处理僵尸进程
        signal(SIGCHLD, SIG_IGN)
        
        # 创建进程
        pid = os.fork()
        if pid < 0:
            print("进程创建失败!")
            return 
        if pid == 0:
            self.__send_msg(name) # 子进程处理函数, 进入发送消息
        else:
            self.__recv_msg(name) # 父进程, 接收消息
        
        
#%% 取消
    # def initialize(self):
    #     """初始化"""

    #     # 循环收发信息
    #     while True:
    #         name = input("取个名字吧!>: ")

    #         msg = self.__msg + name  # 协议+姓名 --> 服务器 --> 返回判断

    #         if name == "exit":
    #             # 如果退出了循环则关闭客户端
    #             self.__sockfd.close()
    #             sys.exit("你决定不进入群聊了.")
    #         self.__sockfd.sendto(msg.encode(), self.__server_ip)  # 发送
    #         data, addr = self.__sockfd.recvfrom(1024)  # 接收

    #         data_ = data.decode().split(' ')[0]  # 分割字符串 进行判断

    #         if data_ == "ok":
    #             print("进入聊天群.", data.decode())
    #             self.chat_group(name)
    #         else:
    #             print("服务器拒绝了: ", data.decode())

    # def chat_group(self, name):
    #     """多线程处理收发消息"""

    #     pid = os.fork()  # 实例化多进程
    #     if pid < 0:
    #         sys.exit(f"进程创建失败: {pid}")
    #     elif pid == 0:
    #         if name == "exit":
    #             sys.exit("正常退出.")
    #         self.send_info(name)  # 发送消息

    #     self.get_info()  # 收取服务器消息

    # def send_info(self, name):
    #     """子进程发送聊天"""
    #     while True:
    #         # 聊天
    #         date = input(f"{name}>: ")

    #         # 封装聊天
    #         lt = self.__group_chat + f"{date} " + name
    #         # print(lt)

    #         if date == "exit":
    #             self.__sockfd.close()
    #             sys.exit("正常退出-->父进程.")

    #         self.__sockfd.sendto(lt.encode(), self.__server_ip)  # 发送

    # def get_info(self):
    #     """父进程接收服务器消息"""
    #     print()
    #     print("子进程创建成功")
    #     data, addr = self.__sockfd.recvfrom(1024)  # 接收服务器消息
    #     jb = data.decode().split(' ')
    #     t, data_, name = jb[0], jb[1], jb[2]  # 解包
    #     # jb = data.decode().split(' ')
    #     # t, data_, name = jb[0], jb[1], jb[2]  # 解包

    #     if t == self.__group_chat:
    #         print(f"群聊{name}: ", data_)
    #     elif t == self.__admin == self.__msg:
    #         print(f"管理员: '{name}'进入群聊.")
    #     elif t == self.__quit:
    #         print(f"管理员: '{name}'退出群聊.")


so = ChatChilent()
so.start()
